# -*- coding: utf-8 -*-
#
# Requirements:
# - Python >= 2.5
# - Twisted (http://twistedmatrix.com/)

import os
import optparse

from twisted.internet import reactor, protocol, stdio, defer
from twisted.protocols import basic
from twisted.internet.protocol import ClientFactory

import simplejson


from utils.common import COMMANDS, display_message, validate_file_md5_hash, get_file_md5_hash, read_bytes_from_file, clean_and_split_input

ROOT_DIR = os.path.dirname(__file__)
DOWNLOADED_FILES_FOLDER = os.path.join(ROOT_DIR, 'downloadedfiles')


SERVER_IP = '127.0.0.1'
SERVER_PORT = 1234


# Client connects to server
# Client sends the channel it is on
# Server sends list of available widgets with md5 so that if there is different versions it still work
# Client looks to see if it has the widgets
# Server sends the widget it doesn't have


# Server registers the different clients. For each, it has the channel currently watching


class ConnectionManager:
    def __init__(self, server_ip, server_port):
        self.factory = FileTransferClientFactory()
        self.connection = reactor.connectTCP(server_ip, server_port, self.factory)
        #self.factory.deferred.addCallback(self.updateWidgets)
        
    def connectServer(self):
        reactor.run()
        
    def updateWidgets(self, msg):
        print "update widgets"


#StateMachine
#State :
    #CONNECTING
    #RETRIEVING_WIDGET
    #CHANGING_CHANNEL
    #EXIT


class FileTransferProtocol(basic.LineReceiver):
    delimiter = '\n'
    
    def __init__(self):
        self.widgets = {}
        self.missingWidgets = []
        self.homebox_available_widgets = []

    def connectionMade(self):
        self.buffer = []
        self.file_handler = None
        self.file_data = ()
        
        print 'Connected to the server'
        
    def connectionLost(self, reason):
        self.file_handler = None
        self.file_data = ()
        
        print 'Connection to the server has been lost'
        #reactor.stop()
        
    
    def getMissingWidgets(self):
        if not self.missingWidgets:
            self.missingWidgets = set(self.homebox_available_widgets) - set(self.widgets.keys())

        
        if self.missingWidgets:
            print "missing %s" % self.missingWidgets
            # We go one by one
            commandtemp = {'action' : 'getfile', 'value' : self.missingWidgets.pop()}
            self.transport.write( '%s\n' % simplejson.dumps(commandtemp) )
        else:
            print "No missing widget"
    
    
    
    def lineReceived(self, line):
        #self.factory.deferred.callback("hihi")
        print line
        try:
            data = simplejson.loads(line)
        except ValueError, e:
            print "can't decode: %s" % e
        
        else:
            command = data['action'].upper()
            
            if command == 'GETCHANNEL':
                print "Server wants to know current channel"
                commandtemp = {'action' : 'setchannel', 'value' : '1'}
                self.transport.write( '%s\n' % simplejson.dumps(commandtemp) )
                
                
            elif command == 'LISTWIDGETS':
                #data = clean_and_split_input(line)
                print "the available widgets for this channels are :"
                
                for widget_infos in data['value']:
                    self.homebox_available_widgets.append( (widget_infos[0], widget_infos[1]) )
                self.getMissingWidgets()
                
            elif command == 'FILETRANSFERT':
                self.file_data = ( data['filename'], data['hash'] )
                self.setRawMode()
                
            elif command == 'ERRORMSG':
                print data['value']
                
                
            elif line == 'ENDMSG':
                print "Received ENDMSG"
                print "Buffer: %s" % self.buffer
                #self.factory.deferred.callback(self.buffer)
                self.buffer = []
            elif line.startswith('HASH'):
                # Received a file name and hash, server is sending us a file
                data = clean_and_split_input(line)

                filename = data[1]
                file_hash = data[2]
                
                self.file_data = (filename, file_hash)
                self.setRawMode()
            else:
                self.buffer.append(line)
        
    def rawDataReceived(self, data):
        filename = self.file_data[0]
        file_path = os.path.join(DOWNLOADED_FILES_FOLDER, filename)
        
        print 'Receiving file chunk (%d KB)' % (len(data))
        
        if not self.file_handler:
            self.file_handler = open(file_path, 'wb')
            
        if data.endswith('\r\n'):
            # Last chunk
            data = data[:-2]
            self.file_handler.write(data)
            self.setLineMode()
            
            self.file_handler.close()
            self.file_handler = None
            
            if validate_file_md5_hash(file_path, self.file_data[1]):
                print 'File %s has been successfully transfered and saved' % (filename)
            else:
                os.unlink(file_path)
                print 'File %s has been successfully transfered, but deleted due to invalid MD5 hash' % (filename)
            self.widgets[ (filename, self.file_data[1]) ] = "cououc"
            self.getMissingWidgets()
        else:
            self.file_handler.write(data)

class FileTransferClientFactory(protocol.ReconnectingClientFactory):
    protocol = FileTransferProtocol
    
    def __init__(self):
        pass
        #self.deferred = defer.Deferred()
        
        
    def connectionMade(self):
        self.resetDelay()
        
    def connectionLost(self, connector, reason):
        protocol.ReconnectingClientFactory.clientConnectionLost(self, connector, reason)
        print 'Connection to the server has been lost (%s)' % reason
        
    def clientConnectionFailed(self, connector, reason):
        print 'Connection failed (%s)' % reason
        protocol.ReconnectingClientFactory.clientConnectionLost(self, connector, reason)    
        

if __name__ == '__main__':
    print 'Client started, incoming files will be saved to %s' % (DOWNLOADED_FILES_FOLDER)
    
    test = ConnectionManager(SERVER_IP, SERVER_PORT)
    test.connectServer()
    